/*
Copyright 2020 Blue Liang, liangkangnan@163.com
                                                                        
Licensed under the Apache License, Version 2.0 (the "License");         
you may not use this file except in compliance with the License.        
You may obtain a copy of the License at                                 
                                                                        
    http://www.apache.org/licenses/LICENSE-2.0                          
                                                                        
Unless required by applicable law or agreed to in writing, software    
distributed under the License is distributed on an "AS IS" BASIS,       
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and     
limitations under the License.                                          
*/


// 串口模块(默认: 115200, 8 N 1)
module uart(
    input wire clk,
    input wire rst,

    input wire we_i,
    input wire[31:0] addr_i,
    input wire[31:0] data_i,

    output reg[31:0] data_o,
    output wire tx_pin,     // 输出 bit 流
    input wire rx_pin       // 输入 bit 流

  );


  // 50MHz时钟，波特率115200bps对应的分频系数
  localparam BAUD_115200 = 32'h1B8;

  localparam S_IDLE       = 4'b0001;
  localparam S_START      = 4'b0010;
  localparam S_SEND_BYTE  = 4'b0100;
  localparam S_STOP       = 4'b1000;

  reg send_en;
  reg[7:0] tx_data;
  wire tx_done;

  wire[7:0] rx_data;
  wire rx_done;

  localparam UART_CTRL = 8'h0;
  localparam UART_STATUS = 8'h4;
  localparam UART_BAUD = 8'h8;
  localparam UART_TXDATA = 8'hc;
  localparam UART_RXDATA = 8'h10;

  // addr: 0x00
  // rw. bit[0]: tx enable, 1 = enable, 0 = disable
  // rw. bit[1]: rx enable, 1 = enable, 0 = disable
  reg[31:0] uart_ctrl;

  // addr: 0x04
  // ro. bit[0]: tx busy, 1 = busy, 0 = idle
  // rw. bit[1]: rx over, 1 = over, 0 = receiving
  // must check this bit before tx data
  reg[31:0] uart_status;

  // addr: 0x08
  // rw. clk div
  reg[31:0] uart_baud;

  // addr: 0x10
  // ro. rx data
  reg[31:0] uart_rx_reg;


  // 写寄存器
  always @ (posedge clk)
  begin
    if (rst == 1'b0)
    begin
      uart_ctrl <= 32'h0;
      uart_status <= 32'h0;
      uart_rx_reg <= 32'h0;
      uart_baud <= BAUD_115200;
      send_en <= 1'b0;
    end
    else
    begin
      if (we_i == 1'b1)
      begin
        case (addr_i[7:0])
          UART_CTRL:
          begin
            uart_ctrl <= data_i;
          end
          UART_BAUD:
          begin
            uart_baud <= data_i;
          end
          UART_STATUS:
          begin
            uart_status[1] <= data_i[1];
          end
          UART_TXDATA:
          begin
            if (uart_ctrl[0] == 1'b1 && uart_status[0] == 1'b0)
            begin
              tx_data <= data_i[7:0];
              uart_status[0] <= 1'b1;
              send_en <= 1'b1;
            end
          end
        endcase
      end
      else
      begin
        send_en <= 1'b0;
        if (tx_done == 1'b1)
        begin
          uart_status[0] <= 1'b0;
        end
        if (uart_ctrl[1] == 1'b1)
        begin
          if (rx_done == 1'b1)
          begin
            uart_status[1] <= 1'b1;
            uart_rx_reg <= {24'h0, rx_data};
          end
        end
      end
    end
  end

  // 读寄存器
  always @ (*)
  begin
    if (rst == 1'b0)
    begin
      data_o = 32'h0;
    end
    else
    begin
      case (addr_i[7:0])
        UART_CTRL:
        begin
          data_o = uart_ctrl;
        end
        UART_STATUS:
        begin
          data_o = uart_status;
        end
        UART_BAUD:
        begin
          data_o = uart_baud;
        end
        UART_RXDATA:
        begin
          data_o = uart_rx_reg;
        end
        default:
        begin
          data_o = 32'h0;
        end
      endcase
    end
  end

  // *************************** TX发送 ****************************
  uart_tx uart_tx_impl(
            .clk(clk),
            .rst_n(rst),
            .send_en(send_en),
            .tx_data(tx_data),
            .rs232_tx(tx_pin),
            .tx_done(tx_done)
          );

  // *************************** RX接收 ****************************
  uart_rx uart_rx_impl(
            .clk(clk),
            .rst_n(rst),
            .rs232_rx(rx_pin),
            .rx_done(rx_done),
            .rx_data(rx_data)
          );
endmodule

module uart_rx
  #(
     parameter SYS_FRENCY =50_000_000 ,//系统频率50M
     parameter BAUD_FRENCY =115200//波特率
   )

   (
     input           clk          ,
     input           rst_n        ,
     input           rs232_rx     ,
     output reg      rx_done      ,
     output reg [7:0]rx_data
   );
  localparam CNT_MAX = SYS_FRENCY/BAUD_FRENCY+4;
  reg        en_cnt          ; //计数器使能控制
  reg [15:0] cnt             ;
  wire       bps_vld         ;  //计数到每bit数据的中心产生一个高电平
  reg [3:0]  bps_cnt         ;  //接受位数的计数器
  reg [1:0]  rs232_rx_filter ;  //用两个寄存器消除亚稳态
  reg        rs232_rx_d;    //用一个寄存器来识别下降沿
  wire       nedge;                //下降沿信号
  reg [9:0]  rx_data_r;
  wire       rx_done_r;
  //消除亚稳态
  always @(posedge clk or negedge rst_n)
  begin
    if(!rst_n)
      rs232_rx_filter<=2'b11;
    else
      rs232_rx_filter<={rs232_rx_filter[0],rs232_rx};
  end
  //下降沿识别
  always @(posedge clk or negedge rst_n)
  begin
    if(!rst_n)
      rs232_rx_d<=1'b1;
    else
      rs232_rx_d<=rs232_rx_filter[1];
  end
  assign nedge=rs232_rx_d&(!rs232_rx_filter[1]);
  //en_cnt
  always @(posedge clk or negedge rst_n)
  begin
    if(!rst_n)
      en_cnt<=1'b0;
    else if(nedge)
      en_cnt<=1'b1;
    else if(rx_done_r)
      en_cnt<=1'b0;
    else
      en_cnt<=en_cnt;
  end
  //cnt
  always @(posedge clk or negedge rst_n)
  begin
    if(!rst_n)
      cnt<='d0;
    else if(en_cnt)
    begin
      if(cnt==CNT_MAX)
        cnt<='d0;
      else
        cnt<=cnt+1'b1;
    end
    else
      cnt<='d0;
  end
  //在中间的时刻采集信号线上的数据，表明数据有效
  assign bps_vld=cnt==(CNT_MAX>>1);
  //bps_cnt  计数到每bit信号的中心然后再读取数值
  always @(posedge clk or negedge rst_n)
  begin
    if(!rst_n)
      bps_cnt<='d0;
    else if(en_cnt)
    begin
      if(cnt==CNT_MAX)
        bps_cnt<=bps_cnt+1'b1;
      else
        bps_cnt<=bps_cnt;
    end
    else
      bps_cnt<='d0;
  end
  //rx_data 读出数据
  always @(posedge clk or negedge rst_n)
  begin
    if(!rst_n)
      rx_data_r<='d0;
    else if (bps_vld)
    begin
      case (bps_cnt)
        0,1,2,3,4,5,6,7,8,9:
          rx_data_r<={rs232_rx_filter[1],rx_data_r[9:1]};
        default:
          rx_data_r<=rx_data_r;
      endcase
    end
    else
      rx_data_r<=rx_data_r;
  end
  //在停止位的的3/4处产生结束信号
  assign rx_done_r=(cnt==CNT_MAX-(CNT_MAX>>2))&&(bps_cnt==4'd9);
  always @(posedge clk or negedge rst_n)
  begin
    if(!rst_n)
      rx_data<='d0;
    else if(rx_done_r)
    begin
      if(!rx_data_r[0]&rx_data_r[9])
        rx_data<=rx_data_r[8:1];
      else
        rx_data<=8'd0;//数据错误置零
    end
    else
      rx_data<=rx_data;
  end
  always @(posedge clk or negedge rst_n)
  begin
    if(!rst_n)
      rx_done<=1'b0;
    else
      rx_done<=rx_done_r;
  end
endmodule

module uart_tx #(
    parameter SYS_FRENCY = 50_000_000 ,//时钟
    parameter BAUD_FRENCY =115200//波特率
  )

  (
    input      clk       ,
    input      rst_n     ,
    input      send_en   ,   //发数据控制信号，脉冲触发
    input [7:0]tx_data   ,
    output reg rs232_tx  ,
    output     tx_done
  );
  localparam  CNT_MAX = SYS_FRENCY/BAUD_FRENCY+4;
  reg        en_cnt   ;
  reg [7:0]  r_tx_data;//用来寄存tx_data
  reg [3:0]  bps_cnt  ;
  reg [15:0] cnt      ;
  //r_tx_data
  always @(posedge clk or negedge rst_n)
  begin
    if(!rst_n)
      r_tx_data<='d0;
    else if(send_en)
      r_tx_data<=tx_data;
    else
      r_tx_data<=r_tx_data;
  end
  //en_cnt
  always @(posedge clk or negedge rst_n)
  begin
    if(!rst_n)
      en_cnt<=1'b0;
    else if(send_en)
      en_cnt<=1'b1;
    else if(cnt==(CNT_MAX>>1)+(CNT_MAX>>2)&&bps_cnt==8'd9)
      en_cnt<=1'b0;
    else
      en_cnt<=en_cnt;
  end
  //bps_cnt
  always @(posedge clk or negedge rst_n)
  begin
    if(!rst_n)
      bps_cnt<='d0;
    else if(en_cnt)
    begin
      if(cnt==CNT_MAX)
        bps_cnt<=bps_cnt+1'b1;
      else
        bps_cnt<=bps_cnt;
    end
    else
      bps_cnt<='d0;
  end
  //cnt
  always @(posedge clk or negedge rst_n)
  begin
    if(!rst_n)
      cnt<='d0;
    else if(en_cnt)
    begin
      if(cnt==CNT_MAX)
        cnt<='d0;
      else
        cnt<=cnt+1'b1;
    end
    else
      cnt<='d0;
  end

  //rs232_tx数据输出
  always @(*)
  begin
    if(!rst_n)
      rs232_tx<=1'b1;
    else if(en_cnt)
    begin
      case (bps_cnt)
        0:
          rs232_tx<=1'b0;
        1:
          rs232_tx<=r_tx_data[0];
        2:
          rs232_tx<=r_tx_data[1];
        3:
          rs232_tx<=r_tx_data[2];
        4:
          rs232_tx<=r_tx_data[3];
        5:
          rs232_tx<=r_tx_data[4];
        6:
          rs232_tx<=r_tx_data[5];
        7:
          rs232_tx<=r_tx_data[6];
        8:
          rs232_tx<=r_tx_data[7];
        9:
          rs232_tx<=1'b1;
        default:
          rs232_tx<=1'b1;
      endcase
    end
    else
      rs232_tx<=1'b1;
  end
  assign tx_done=(cnt==(CNT_MAX>>1)+(CNT_MAX>>2))&&bps_cnt==4'd9;
endmodule
